package com.atguigu.tingshu.user.service.impl;

import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.api.WxMaUserService;
import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.IdUtil;
import com.atguigu.tingshu.album.AlbumFeignClient;
import com.atguigu.tingshu.common.cache.GuiGuCache;
import com.atguigu.tingshu.common.constant.RedisConstant;
import com.atguigu.tingshu.common.execption.GuiguException;
import com.atguigu.tingshu.common.rabbit.constant.MqConst;
import com.atguigu.tingshu.common.rabbit.service.RabbitService;
import com.atguigu.tingshu.common.util.AuthContextHolder;
import com.atguigu.tingshu.common.util.IpUtil;
import com.atguigu.tingshu.model.user.UserInfo;
import com.atguigu.tingshu.model.user.UserPaidAlbum;
import com.atguigu.tingshu.model.user.UserPaidTrack;
import com.atguigu.tingshu.user.mapper.*;
import com.atguigu.tingshu.user.pattern.DeliveryStrategy;
import com.atguigu.tingshu.user.pattern.factory.DeliveryStrategyFactory;
import com.atguigu.tingshu.user.service.UserInfoService;
import com.atguigu.tingshu.vo.user.UserInfoVo;
import com.atguigu.tingshu.vo.user.UserPaidRecordVo;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.error.WxErrorException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import java.math.BigDecimal;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Slf4j
@Service
@SuppressWarnings({"all"})
public class UserInfoServiceImpl extends ServiceImpl<UserInfoMapper, UserInfo> implements UserInfoService {

    @Autowired
    private UserInfoMapper userInfoMapper;

    @Autowired
    private WxMaService wxMaService;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private RabbitService rabbitService;

    @Autowired
    private UserPaidAlbumMapper userPaidAlbumMapper;

    @Autowired
    private UserPaidTrackMapper userPaidTrackMapper;

    @Autowired
    private MongoTemplate mongoTemplate;

    @Autowired
    private AlbumFeignClient albumFeignClient;

    @Autowired
    private UserVipServiceMapper userVipServiceMapper;

    @Autowired
    private VipServiceConfigMapper vipServiceConfigMapper;


    /**
     * 微信一键登录
     *
     * @param code 小程序端根据当前微信，生成访问为微信服务端临时凭据
     * @return
     */
    @Override
    public Map<String, String> wxLogin(String code) {
        try {
            //1.拿着临时凭据+应用ID+应用秘钥 调用微信接口 获取当前微信账户唯一标识：openId
            //1.1 微信账户信息业务类
            WxMaUserService userService = wxMaService.getUserService();
            //1.2 获取会话信息
            WxMaJscode2SessionResult sessionInfo = userService.getSessionInfo(code);
            //1.3 获取微信账号唯一标识
            String openid = sessionInfo.getOpenid();

            //2.根据微信账户唯一标识，查询数据库，看当前微信是否已经注册
            UserInfo userInfo = userInfoMapper.selectOne(
                    new LambdaQueryWrapper<UserInfo>()
                            .eq(UserInfo::getWxOpenId, openid)
            );

            //3.如果微信账户首次注册，则新增用户记录，为用户初始化账户记录用于后续订单支付
            if (userInfo == null) {
                //3.1 新增用户记录 绑定微信账户唯一标识
                userInfo = new UserInfo();
                userInfo.setWxOpenId(openid);
                userInfo.setNickname(IdUtil.nanoId());
                userInfo.setAvatarUrl("http://192.168.200.6:9000/tingshu/2024-04-02/0b033705-4603-4fb2-bd0f-db84076aef84.jpg");
                userInfoMapper.insert(userInfo);
                //3.2 TODO 为当前注册用户初始化账户记录
                // 方案一：Openfeign远程调用 分布式事务问题  方案二：采用MQ可靠性消息队列实现数据最终一致
                //3.2.1 构建消息对象 注意：如果是VO对象一定要实现序列化接口以及生成序列化版本号
                Map<String, Object> map = new HashMap<>();
                map.put("userId", userInfo.getId());
                map.put("amount", new BigDecimal("100"));
                map.put("title", "新用户专项体验金活动");
                map.put("orderNo", "ZS" + IdUtil.getSnowflakeNextId());
                //3.2.2 发送消息到MQ
                rabbitService.sendMessage(MqConst.EXCHANGE_USER, MqConst.ROUTING_USER_REGISTER, map);
            }

            //4.基于当前用户信息生成令牌
            //4.1 创建令牌
            String token = IdUtil.randomUUID();
            //4.2 构建登录成功后Redis的Key 形式为：user:login:token
            String loginKey = RedisConstant.USER_LOGIN_KEY_PREFIX + token;
            //4.3 构建登录成功后Redis的Value 形式为：userInfo
            UserInfoVo userInfoVo = BeanUtil.copyProperties(userInfo, UserInfoVo.class);
            //4.4 TODO 调用百度地图接口，获取登录用户行政区域（根据客户端IP获取）
            RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
            //1.2 将接口类型强转为ServletRequestAttributes实现类
            ServletRequestAttributes sra = (ServletRequestAttributes) requestAttributes;
            //1.3 获取请求对象
            HttpServletRequest request = sra.getRequest();
            String ipAddress = IpUtil.getIpAddress(request);

            userInfoVo.setLoginRegin("北京市");
            //4.4 存入Redis 设置有效期：7天
            redisTemplate.opsForValue().set(loginKey, userInfoVo, RedisConstant.USER_LOGIN_KEY_TIMEOUT, TimeUnit.SECONDS);

            //5.封装令牌返回给前端
            Map<String, String> map = new HashMap<>();
            map.put("token", token);
            return map;
        } catch (WxErrorException e) {
            log.error("微信登录失败");
            throw new GuiguException(500, "微信登录失败");
        }
    }

    /**
     * 根据用户ID查询用户基本信息
     *
     * @param userId
     * @return
     */
    @Override
    @GuiGuCache(prefix = "user:info:")
    public UserInfoVo getUserInfo(Long userId) {
        UserInfo userInfo = userInfoMapper.selectById(userId);
        if (userInfo != null) {
            return BeanUtil.copyProperties(userInfo, UserInfoVo.class);
        }
        return null;
    }

    /**
     * 更新用户信息方法
     * 只允许修改昵称头像
     *
     * @param userInfoVo
     */
    @Override
    public void updateUser(UserInfoVo userInfoVo) {
        //一、修改DB
        //1.获取用户ID
        Long userId = AuthContextHolder.getUserId();
        //2.更新用户昵称跟头像
        UserInfo userInfo = new UserInfo();
        userInfo.setId(userId);
        userInfo.setAvatarUrl(userInfoVo.getAvatarUrl());
        userInfo.setNickname(userInfoVo.getNickname());
        userInfoMapper.updateById(userInfo);
        //二、删除缓存
    }


    /**
     * 提交需要检查购买状态声音ID列表，响应每个声音购买状态
     *
     * @param userId                        用户ID
     * @param albumId                       专辑ID
     * @param needCheckPayStatusTrackIdList 待检查购买状态声音ID列表
     * @return
     */
    @Override
    public Map<Long, Integer> userIsPaidTrack(Long userId, Long albumId, List<Long> needCheckPayStatusTrackIdList) {
        Map<Long, Integer> map = new HashMap<>();
        //1.根据用户ID+专辑ID查询专辑购买记录
        Long count = userPaidAlbumMapper.selectCount(
                new LambdaQueryWrapper<UserPaidAlbum>()
                        .eq(UserPaidAlbum::getUserId, userId)
                        .eq(UserPaidAlbum::getAlbumId, albumId)
        );

        //2. 如果已购买专辑，将所有待检查购买状态声音 购买状态设置为 1 响应
        if (count > 0) {
            for (Long trackId : needCheckPayStatusTrackIdList) {
                map.put(trackId, 1);
            }
            return map;
        }

        //3. 根据用户ID+专辑ID查询已购声音记录
        List<UserPaidTrack> userPaidTrackList = userPaidTrackMapper.selectList(
                new LambdaQueryWrapper<UserPaidTrack>()
                        .eq(UserPaidTrack::getUserId, userId)
                        .eq(UserPaidTrack::getAlbumId, albumId)
                        .select(UserPaidTrack::getTrackId)
        );

        //4. 如果不存再已购声音，将所有待检查购买状态声音 购买状态设置为 0 响应
        if (CollUtil.isEmpty(userPaidTrackList)) {
            for (Long trackId : needCheckPayStatusTrackIdList) {
                map.put(trackId, 0);
            }
            return map;
        }


        //5.如果存在已购声音，将提交检查声音ID列表中，已购声购买状态设置为：1。未购买设置为0
        List<Long> userPaidTrackIdList = userPaidTrackList.stream().map(UserPaidTrack::getTrackId).collect(Collectors.toList());
        for (Long trackId : needCheckPayStatusTrackIdList) {
            if (userPaidTrackIdList.contains(trackId)) {
                map.put(trackId, 1);
            } else {
                map.put(trackId, 0);
            }
        }
        return map;
    }

    /**
     * 判断当前用户是否购买过指定专辑
     *
     * @param albumId
     * @return
     */
    @Override
    public Boolean isPaidAlbum(Long userId, Long albumId) {
        Long count = userPaidAlbumMapper.selectCount(
                new LambdaQueryWrapper<UserPaidAlbum>()
                        .eq(UserPaidAlbum::getUserId, userId)
                        .eq(UserPaidAlbum::getAlbumId, albumId)
        );
        return count > 0;
    }

    /**
     * 查询当前用户某个专辑下已购声音ID列表
     *
     * @param albumId
     * @return
     */
    @Override
    public List<Long> findUserPaidTrackList(Long userId, Long albumId) {
        List<UserPaidTrack> userPaidTrackList = userPaidTrackMapper.selectList(
                new LambdaQueryWrapper<UserPaidTrack>()
                        .eq(UserPaidTrack::getUserId, userId)
                        .eq(UserPaidTrack::getAlbumId, albumId)
                        .select(UserPaidTrack::getTrackId)
        );
        if (CollUtil.isNotEmpty(userPaidTrackList)) {
            List<Long> userPaidTrackIdList = userPaidTrackList.stream()
                    .map(UserPaidTrack::getTrackId)
                    .collect(Collectors.toList());
            return userPaidTrackIdList;
        }
        return null;
    }

    @Autowired
    private DeliveryStrategyFactory deliveryStrategyFactory;

    /**
     * 用户支付成功后，虚拟物品发货
     *
     * @param userPaidRecordVo
     * @return
     */
    @Override
    public void savePaidRecord(UserPaidRecordVo userPaidRecordVo) {
        //TODO 策略模式+工厂模式优化
        //1. 根据商品类型从工厂中返回具体策略实现类对象
        DeliveryStrategy strategy = deliveryStrategyFactory.getStrategy(userPaidRecordVo.getItemType());
        //2. 调用策略实现类对象进行虚拟物品发货逻辑
        strategy.delivery(userPaidRecordVo);
/*
        //付款项目类型: 1001-专辑 1002-声音 1003-vip会员
        String itemType = userPaidRecordVo.getItemType();
        //1.处理虚拟物品"专辑"发货逻辑
        if (SystemConstant.ORDER_ITEM_TYPE_ALBUM.equals(itemType)) {
            //1.1 根据订单编号查询已购专辑记录，如果存在，则返回
            Long count = userPaidAlbumMapper.selectCount(
                    new LambdaQueryWrapper<UserPaidAlbum>()
                            .eq(UserPaidAlbum::getOrderNo, userPaidRecordVo.getOrderNo())
            );
            if (count > 0) {
                log.info("已存在该订单:{}的已购专辑记录", userPaidRecordVo.getOrderNo());
                return;
            }
            //1.2 如果不存在，则构建专辑已购记录完成保存
            UserPaidAlbum userPaidAlbum = new UserPaidAlbum();
            userPaidAlbum.setOrderNo(userPaidRecordVo.getOrderNo());
            userPaidAlbum.setUserId(userPaidRecordVo.getUserId());
            userPaidAlbum.setAlbumId(userPaidRecordVo.getItemIdList().get(0));
            userPaidAlbumMapper.insert(userPaidAlbum);
        } else if (SystemConstant.ORDER_ITEM_TYPE_TRACK.equals(itemType)) {
            //2. 处理虚拟物品"声音"发货逻辑
            //2.1 根据订单编号查询已购声音记录，如果存在，则返回
            Long count = userPaidTrackMapper.selectCount(
                    new LambdaQueryWrapper<UserPaidTrack>()
                            .eq(UserPaidTrack::getOrderNo, userPaidRecordVo.getOrderNo())
            );
            if (count > 0) {
                log.info("已存在该订单:{}的已购声音记录", userPaidRecordVo.getOrderNo());
                return;
            }
            //2.2 如果不存在，则构建声音已购记录完成保存
            TrackInfo trackInfo = albumFeignClient.getTrackInfo(userPaidRecordVo.getItemIdList().get(0)).getData();
            List<Long> trackIdList = userPaidRecordVo.getItemIdList();
            for (Long trackId : trackIdList) {
                UserPaidTrack userPaidTrack = new UserPaidTrack();
                userPaidTrack.setOrderNo(userPaidRecordVo.getOrderNo());
                userPaidTrack.setUserId(userPaidRecordVo.getUserId());
                userPaidTrack.setAlbumId(trackInfo.getAlbumId());
                userPaidTrack.setTrackId(trackId);
                userPaidTrackMapper.insert(userPaidTrack);
            }
        } else if (SystemConstant.ORDER_ITEM_TYPE_VIP.equals(itemType)) {
            //3. 处理虚拟物品"会员"发货逻辑
            //3.1 根据订单编号查询已购会员记录，如果存在，则返回
            Long count = userVipServiceMapper.selectCount(
                    new LambdaQueryWrapper<UserVipService>()
                            .eq(UserVipService::getOrderNo, userPaidRecordVo.getOrderNo())
            );
            if (count > 0) {
                return;
            }
            //3.2 判断当前用户是否为会员
            Boolean isVIP = false;
            UserInfoVo userInfoVo   = this.getUserInfo(userPaidRecordVo.getUserId());
            Date now = new Date();
            if (userInfoVo.getIsVip().intValue() == 1 && userInfoVo.getVipExpireTime().after(now)) {
                isVIP = true;
            }
            //3.3 新增会员购买记录
            UserVipService userVipService = new UserVipService();
            userVipService.setOrderNo(userPaidRecordVo.getOrderNo());
            userVipService.setUserId(userPaidRecordVo.getUserId());
            //3.3.1 获取会员套餐信息得到服务月数
            VipServiceConfig vipServiceConfig = vipServiceConfigMapper.selectById(userPaidRecordVo.getItemIdList().get(0));
            Integer serviceMonth = vipServiceConfig.getServiceMonth();
            //3.3.2 计算本次会员购买记录生效时间
            //3.3.3 计算本次会员购买记录失效时间
            if (!isVIP) {
                //普通用户，生效时间为当前时间 失效时间=当前时间+购买会员套餐月数
                userVipService.setStartTime(now);
                DateTime expireTime = DateUtil.offsetMonth(now, serviceMonth);
                userVipService.setExpireTime(expireTime);
            } else {
                //会员用户，生效时间为=会员的到期时间+1 失效时间=会员的到期时间+购买会员套餐月数
                Date vipExpireTime = userInfoVo.getVipExpireTime();  //6.14
                userVipService.setStartTime(DateUtil.offsetDay(vipExpireTime, 1)); //6.15
                userVipService.setExpireTime(DateUtil.offsetMonth(userVipService.getStartTime(), serviceMonth)); //7.15
            }
            userVipServiceMapper.insert(userVipService);
            //3.4 更新用户会员标识以及更新会员过期时间
            userInfoVo.setIsVip(1);
            userInfoVo.setVipExpireTime(userVipService.getExpireTime());
            UserInfo userInfo = BeanUtil.copyProperties(userInfoVo, UserInfo.class);
            userInfoMapper.updateById(userInfo);
        }else if(SystemConstant.ORDER_ITEM_TYPE_SVIP.equals(itemType)){
            // TODO
        }*/
    }

    /**
     * 更新VIP状态：处理过期会员
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateVipExpireStatus(Date date) {
        //1.查询已过期会员列表
        List<UserInfo> userInfoList = userInfoMapper.selectList(
                new LambdaQueryWrapper<UserInfo>()
                        .select(UserInfo::getId)
                        .eq(UserInfo::getIsVip, 1)
                        .lt(UserInfo::getVipExpireTime, date)
        );

        //2.更新会员标识为0
        if(CollUtil.isNotEmpty(userInfoList)){
            userInfoList.stream().forEach(userInfo -> {
                userInfo.setIsVip(0);
                userInfoMapper.updateById(userInfo);
            });
        }
    }
}
